package org.jiajie.coffeecodes.concurrent.pattern.guarded.suspension;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;

/**
 * @author jay
 */
public class GuardedObject<T> {
    /**
     * 受保护的对象
     */
    T obj;
    final Lock lock = new ReentrantLock();
    final Condition done = lock.newCondition();
    final int timeout = 2;

    /**
     * 保存所有GuardedObject
     */
    final static Map<Object, GuardedObject> gos = new ConcurrentHashMap<>();

    /**
     * 静态方法创建GuardedObject
     * @param key   key
     * @param <K>   K
     * @return  GuardedObject
     */
    static <K> GuardedObject create(K key) {
        GuardedObject go = new GuardedObject();
        gos.put(key, go);
        return go;
    }

    static <K, T> void fireEvent(K key, T obj) {
        GuardedObject go = gos.remove(key);
        if (go != null) {
            go.onChanged(obj);
        }
    }

    /**
     * 获取受保护的对象
     * @param p p
     * @return  受保护的对象
     */
    T get(Predicate<T> p){
        lock.lock();
        try {
            // MESA管程推荐写法
            while (!p.test(obj)){
                done.await(timeout, TimeUnit.SECONDS);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
        return obj;
    }

    /**
     * 时间通知方法
     * @param obj   object
     */
    void onChanged(T obj){
        lock.lock();
        try {
            this.obj = obj;
            done.signalAll();
        } finally {
            lock.unlock();
        }
    }
}
